package com.dysoft.flesh.gl;

import com.dysoft.flesh.RenderContext;
import com.sun.opengl.util.texture.TextureIO;

import javax.media.opengl.GL;
import javax.media.opengl.GLContext;
import java.io.FileInputStream;
import java.util.concurrent.Callable;

import com.sun.opengl.util.texture.Texture;

/**
 * @author Sean Micklethwaite
 *         Aug 14, 2010 9:35:52 PM
 */
public class GLTexture extends GLDataObject<com.dysoft.flesh.Texture> implements com.dysoft.flesh.Texture {
	public GLTexture(final String path) {
		super(path, new Callable<com.dysoft.flesh.Texture>() {
			public com.dysoft.flesh.Texture call() throws Exception {
				return new Impl(TextureIO.newTexture(new FileInputStream(path), true, null));
			}
		}, null);
	}

	public void bind(RenderContext ctx, int unit) throws NotPresentException {
		id.setContext(ctx);
		get().bind(ctx,unit);
	}

	public void unbind(RenderContext ctx, int unit) throws NotPresentException {
		get().unbind(ctx, unit);
		id.setContext(null);
	}

	public void render(RenderContext ctx) throws NotPresentException {
		renderQuad(ctx, this);
	}

	static class Impl extends GLObject implements com.dysoft.flesh.Texture {
		final Texture texture;

		public Impl(com.sun.opengl.util.texture.Texture texture) {
			super(texture.getTextureObject());
			texture.setTexParameteri( GL.GL_TEXTURE_WRAP_S, GL.GL_REPEAT );
			texture.setTexParameteri( GL.GL_TEXTURE_WRAP_T, GL.GL_REPEAT );
			this.texture = texture;
		}
		public void bind(RenderContext ctx, int unit) throws NotPresentException {
			GLContext.getCurrent().getGL().glActiveTexture(GL.GL_TEXTURE0 + unit);
			texture.bind();
		}

		public void unbind(RenderContext ctx, int unit) throws NotPresentException {
			// TODO: stack
		}

		public void render(RenderContext ctx) throws NotPresentException {
			renderQuad(ctx, this);
		}
	}

	public static void renderQuad(RenderContext ctx, com.dysoft.flesh.Texture tex) throws NotPresentException {
		GL gl = ((GLRenderer.GLRenderContext)ctx).getGL();
		boolean enabled = gl.glIsEnabled(GL.GL_TEXTURE_2D);
		if(!enabled)
			gl.glEnable(GL.GL_TEXTURE_2D);

		tex.bind(ctx, 0);

		gl.glBegin(GL.GL_QUADS);
        gl.glTexCoord2d(0.0, 1.0);
        gl.glVertex2d(0, 0);
        gl.glTexCoord2d(0.0, 0.0);
        gl.glVertex2d(0, 1);
        gl.glTexCoord2d(1.0, 0.0);
        gl.glVertex2d(1, 1);
        gl.glTexCoord2d(1.0, 1.0);
        gl.glVertex2d(1, 0);
        gl.glEnd();

		tex.unbind(ctx, 0);

		if(!enabled)
			gl.glDisable(GL.GL_TEXTURE_2D);
	}

	public static int getTextureFormat(int bpp, int channels, boolean floatFmt)
		throws Exception
	{
		int fmt = -1;
		if(floatFmt) {
			// TODO: check for ARB_texture_float
			switch(channels) {
				case 1:
					// TODO: check for NV_float_buffer
					switch(bpp) {
						case 16:
							fmt = GL.GL_FLOAT_R16_NV;
							break;
						case 32:
							fmt = GL.GL_FLOAT_R32_NV;
							break;
					}
					break;
				case 2:
					switch(bpp) {
						case 32:
							fmt = GL.GL_FLOAT_RG16_NV;
							break;
						case 64:
							fmt = GL.GL_FLOAT_RG32_NV;
							break;
					}
					break;
				case 3:
					switch(bpp) {
						case 32:
							// XXX: no sign bit - should this be allowed?
							//fmt = GL.GL_R11F_G11F_B10F_EXT;
							break;
						case 48:
							fmt = GL.GL_RGB16F_ARB;
							break;
						case 96:
							fmt = GL.GL_RGB32F_ARB;
					}
				case 4:
					switch (bpp) {
						case 64:
							fmt = GL.GL_RGBA16F_ARB;
							break;
						case 128:
							fmt = GL.GL_RGBA32F_ARB;
							break;
					}
					break;
			}
		} else {
			switch(channels) {
				case 3:
					switch (bpp) {
						case 8:
							fmt = GL.GL_R3_G3_B2;
							break;
						case 12:
							fmt = GL.GL_RGB4;
							break;
						case 24:
							fmt = GL.GL_RGB12;
							break;
						case 32:
							fmt = GL.GL_RGB10_A2;
							break;
						case 36:
							fmt = GL.GL_RGB12;
							break;
						case 48:
							fmt = GL.GL_RGB16;
							break;
					}
					break;
				case 4:
					switch(bpp) {
						case 8:
							fmt = GL.GL_RGBA2;
							break;
						case 16:
							fmt = GL.GL_RGBA4;
							break;
						case 32:
							fmt = GL.GL_RGBA8;
							break;
						case 48:
							fmt = GL.GL_RGBA12;
							break;
						case 64:
							fmt = GL.GL_RGBA16;
							break;
					}
					break;
			}
		}

		if(fmt == -1) {
			throw new Exception("Unsupported texture format: " + bpp + "bpp / " + channels
				+ ", " + (floatFmt ? "floating point" : "integer"));
		}

		return fmt;
	}
}
